//
//  ZClient.m
//  LuckyTools
//
//  Created by ZhàoYǒng on 16/3/1.
//  Copyright © 2016年 ZhàoYǒng. All rights reserved.
//

#import "ZClient.h"
#import <Mantle/Mantle.h>
#import "ZServer.h"

NSString *const ZClientErrorDomain = @"ZClientErrorDomain";
const NSInteger ZClientErrorJSONParsingFailed = 1019;

@implementation ZClient

- (instancetype)init {
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    config.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
    config.timeoutIntervalForRequest = 20.f;
    config.timeoutIntervalForResource = 30.f;
    self = [super initWithBaseURL:[NSURL URLWithString:ZServer.server.serverURL] sessionConfiguration:config];
    if (self) {
        self.requestSerializer = [AFJSONRequestSerializer serializer];
        [self.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        self.responseSerializer = [AFJSONResponseSerializer serializer];
        self.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/plain", @"text/html", nil];
    }
    return self;
}

#pragma mark - NSMutableURLRequest

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters constructingBodyWithBlock:(void(^)(id <AFMultipartFormData> formData))block {
    
    if ([self.clientDelegate respondsToSelector:@selector(clientReformParameters:)]) {
        parameters = [self.clientDelegate clientReformParameters:parameters];
    }
    
    NSURL *URL = path ? [NSURL URLWithString:path relativeToURL:self.baseURL] : self.baseURL;
    NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:method URLString:URL.absoluteString parameters:parameters constructingBodyWithBlock:block error:nil];
    
    if ([self.clientDelegate respondsToSelector:@selector(headerFields)]) {
        NSMutableDictionary *headers = [NSMutableDictionary dictionaryWithDictionary:request.allHTTPHeaderFields];
        NSDictionary *customHeaders = [self.clientDelegate headerFields];
        for (NSString *key in customHeaders.allKeys) {
            id value = customHeaders[key];
            [headers setObject:value forKey:key];
        }
        request.allHTTPHeaderFields = headers;
    }
    
    return request;
}

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(id)parameters {
    
    if ([self.clientDelegate respondsToSelector:@selector(clientReformParameters:)]) {
        parameters = [self.clientDelegate clientReformParameters:parameters];
    }
    
    NSURL *URL = path ? [NSURL URLWithString:path relativeToURL:self.baseURL] : self.baseURL;
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:URL.absoluteString parameters:parameters error:nil];
    
    if ([self.clientDelegate respondsToSelector:@selector(headerFields)]) {
        NSMutableDictionary *headers = [NSMutableDictionary dictionaryWithDictionary:request.allHTTPHeaderFields];
        NSDictionary *customHeaders = [self.clientDelegate headerFields];
        for (NSString *key in customHeaders.allKeys) {
            id value = customHeaders[key];
            [headers setObject:value forKey:key];
        }
        request.allHTTPHeaderFields = headers;
    }
    
    return request;
}

#pragma mark - Public

- (RACSignal *)enqueueRequest:(NSURLRequest *)request resultClass:(Class)resultClass {
    return [[[self enqueueRequest:request] reduceEach:^(NSURLResponse *URLResponse, id responseObject) {
        if ([self.clientDelegate respondsToSelector:@selector(parseResponse:)]) {
            ZParsedResponse *parsedResponse = [self.clientDelegate parseResponse:responseObject];
            if (parsedResponse.error) return [RACSignal error:parsedResponse.error];
            return [self parsedResponseOfClass:resultClass fromJSON:parsedResponse.parsedObject];
        } else {
            return [RACSignal return:responseObject];
        }
    }] concat];
}

#pragma mark - Private

- (RACSignal *)enqueueRequest:(NSURLRequest *)request {
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSURLSessionDataTask *task = [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
#if DEBUG
            NSLog(@">>>>>%@ request: %@\n>>>>>response: %@\n>>>>>error: %@", request.HTTPMethod, request.URL, responseObject, error.description);
#endif
            if (error) {
#if DEBUG
                NSLog(@">>>>>%@ request: %@\n>>>>>error: %@", request.HTTPMethod, request.URL, error.description);
#endif
                [subscriber sendError:[NSError errorWithDomain:ZClientErrorDomain code:0 userInfo:@{NSLocalizedFailureReasonErrorKey : @"网络请求异常"}]];
                return;
            }
            [[RACSignal return:RACTuplePack(response, responseObject)] subscribe:subscriber];
        }];
        [task resume];
        return [RACDisposable disposableWithBlock:^{ [task cancel]; }];
    }];
    return [[signal replayLazily] setNameWithFormat:@"`enqueueRequest: %@`", request];
}

- (RACSignal *)parsedResponseOfClass:(Class)resultClass fromJSON:(id)responseObject {
    NSParameterAssert(!resultClass || [resultClass isSubclassOfClass:MTLModel.class]);
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        void(^parseJSON)(id) = ^(id object) {
            if (!resultClass) {
                [subscriber sendNext:object];
                return;
            }
            NSError *error = nil;
            id parsedObject = nil;
            if ([object isKindOfClass:NSDictionary.class]) {
                parsedObject = [MTLJSONAdapter modelOfClass:resultClass fromJSONDictionary:object error:&error];
            } else {
                parsedObject = [MTLJSONAdapter modelsOfClass:resultClass fromJSONArray:object error:&error];
            }
            if (!parsedObject) {
                if (![error.domain isEqual:MTLJSONAdapterErrorDomain] || error.code != MTLJSONAdapterErrorNoClassFound) {
                    [subscriber sendError:error];
                }
                return;
            }
            [subscriber sendNext:parsedObject];
        };
        
        if ([responseObject isKindOfClass:NSArray.class] || [responseObject isKindOfClass:NSDictionary.class]) {
            parseJSON(responseObject);
            [subscriber sendCompleted];
        } else if (!responseObject || [responseObject isKindOfClass:NSNull.class]) {
            [subscriber sendNext:nil];
            [subscriber sendCompleted];
        } else if ([responseObject isKindOfClass:NSString.class]) {
            id obj = [NSJSONSerialization JSONObjectWithData:[responseObject dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil];
            if (obj) {
                parseJSON(obj);
                [subscriber sendCompleted];
            } else {
                [subscriber sendNext:responseObject];
                [subscriber sendCompleted];
            }
        } else if ([responseObject isKindOfClass:NSNumber.class]) {
            [subscriber sendNext:responseObject];
            [subscriber sendCompleted];
        } else {
            [subscriber sendError:[NSError errorWithDomain:ZClientErrorDomain code:ZClientErrorJSONParsingFailed userInfo:@{NSLocalizedFailureReasonErrorKey : @"返回参数格式错误"}]];
        }
        return [RACDisposable disposableWithBlock:^{}];

    }];
}

@end
